use serde::{Deserialize, Serialize};
use regex::Regex;
use std::fs::File;
use std::io::Write;
use std::sync::Mutex;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VideoInfo {
    pub bvid: String,
    pub title: String,
    pub cover: String,
    pub desc: String,
    pub duration: u64,
    pub author: String,
    pub cid: u64,
    pub download_url: Option<String>,
    pub collection_title: Option<String>, // 合集标题
}

#[derive(Debug, Deserialize)]
struct BilibiliApiResponse {
    code: i32,
    data: Option<BilibiliVideoData>,
    message: Option<String>,
}

#[derive(Debug, Deserialize)]
struct BilibiliVideoData {
    bvid: String,
    title: String,
    pic: String,
    desc: String,
    duration: u64,
    owner: BilibiliOwner,
    pages: Vec<BilibiliPage>,
}

#[derive(Debug, Deserialize)]
struct BilibiliOwner {
    name: String,
}

#[derive(Debug, Deserialize)]
struct BilibiliPage {
    cid: u64,
    page: u32,
    part: String,
}

#[derive(Debug, Deserialize)]
struct BilibiliPlayUrlResponse {
    code: i32,
    data: Option<BilibiliPlayUrlData>,
}

#[derive(Debug, Deserialize)]
struct BilibiliPlayUrlData {
    durl: Option<Vec<BilibiliDurl>>,
}

#[derive(Debug, Deserialize)]
struct BilibiliDurl {
    url: String,
}

// 从URL提取BV号
fn extract_bvid(url: &str) -> Result<String, String> {
    let re = Regex::new(r"(BV[a-zA-Z0-9]+)").map_err(|e| e.to_string())?;
    
    if let Some(caps) = re.captures(url) {
        if let Some(bvid) = caps.get(1) {
            return Ok(bvid.as_str().to_string());
        }
    }
    
    Err("无法从URL中提取BV号".to_string())
}

// 解析B站视频信息
#[tauri::command]
async fn parse_bilibili_video(url: String) -> Result<Vec<VideoInfo>, String> {
    let bvid = extract_bvid(&url)?;
    
    // 构建API URL
    let api_url = format!(
        "https://api.bilibili.com/x/web-interface/view?bvid={}",
        bvid
    );
    
    // 创建HTTP客户端
    let client = reqwest::Client::builder()
        .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
        .build()
        .map_err(|e| format!("创建HTTP客户端失败: {}", e))?;
    
    // 发送请求
    let response = client
        .get(&api_url)
        .send()
        .await
        .map_err(|e| format!("请求失败: {}", e))?;
    
    // 解析响应
    let api_response: BilibiliApiResponse = response
        .json()
        .await
        .map_err(|e| format!("解析响应失败: {}", e))?;
    
    if api_response.code != 0 {
        return Err(format!(
            "API返回错误: {}",
            api_response.message.unwrap_or("未知错误".to_string())
        ));
    }
    
    let data = api_response
        .data
        .ok_or("API返回数据为空".to_string())?;
    
    // 构建视频信息列表
    let mut videos = Vec::new();
    let collection_title = data.title.clone();
    
    if data.pages.len() == 1 {
        // 单P视频
        videos.push(VideoInfo {
            bvid: data.bvid.clone(),
            title: data.title.clone(),
            cover: data.pic.clone(),
            desc: data.desc.clone(),
            duration: data.duration,
            author: data.owner.name.clone(),
            cid: data.pages[0].cid,
            download_url: None,
            collection_title: Some(collection_title.clone()),
        });
    } else {
        // 多P视频
        for page in &data.pages {
            videos.push(VideoInfo {
                bvid: data.bvid.clone(),
                title: format!("P{}: {}", page.page, page.part),
                cover: data.pic.clone(),
                desc: data.desc.clone(),
                duration: data.duration,
                author: data.owner.name.clone(),
                cid: page.cid,
                download_url: None,
                collection_title: Some(collection_title.clone()),
            });
        }
    }
    
    Ok(videos)
}

// 获取视频下载地址
async fn get_download_url(bvid: &str, cid: u64) -> Result<String, String> {
    let api_url = format!(
        "https://api.bilibili.com/x/player/playurl?bvid={}&cid={}&qn=80&fnval=0",
        bvid, cid
    );
    
    let client = reqwest::Client::builder()
        .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
        .build()
        .map_err(|e| format!("创建HTTP客户端失败: {}", e))?;
    
    let response = client
        .get(&api_url)
        .header("Referer", "https://www.bilibili.com")
        .send()
        .await
        .map_err(|e| format!("请求失败: {}", e))?;
    
    let play_response: BilibiliPlayUrlResponse = response
        .json()
        .await
        .map_err(|e| format!("解析响应失败: {}", e))?;
    
    if play_response.code != 0 {
        return Err("获取播放地址失败".to_string());
    }
    
    let data = play_response.data.ok_or("播放数据为空".to_string())?;
    let durl = data.durl.ok_or("下载地址为空".to_string())?;
    
    if durl.is_empty() {
        return Err("下载地址列表为空".to_string());
    }
    
    Ok(durl[0].url.clone())
}

// 全局下载路径
static DOWNLOAD_PATH: Mutex<Option<String>> = Mutex::new(None);

// 获取下载目录路径
#[tauri::command]
fn get_download_path() -> Result<String, String> {
    let path_lock = DOWNLOAD_PATH.lock().map_err(|e| e.to_string())?;
    
    if let Some(path) = path_lock.as_ref() {
        return Ok(path.clone());
    }
    
    let download_dir = dirs::download_dir()
        .ok_or("无法获取下载目录".to_string())?;
    Ok(download_dir.to_string_lossy().to_string())
}

// 选择下载文件夹
#[tauri::command]
fn select_download_folder() -> Result<String, String> {
    use rfd::FileDialog;
    
    let folder = FileDialog::new()
        .set_title("选择视频保存位置")
        .pick_folder();
    
    if let Some(path) = folder {
        let path_str = path.to_string_lossy().to_string();
        let mut path_lock = DOWNLOAD_PATH.lock().map_err(|e| e.to_string())?;
        *path_lock = Some(path_str.clone());
        Ok(path_str)
    } else {
        Err("未选择文件夹".to_string())
    }
}

// 打开文件所在文件夹
#[tauri::command]
fn open_download_folder() -> Result<(), String> {
    let path_lock = DOWNLOAD_PATH.lock().map_err(|e| e.to_string())?;
    let download_dir = if let Some(custom_path) = path_lock.as_ref() {
        std::path::PathBuf::from(custom_path)
    } else {
        dirs::download_dir().ok_or("无法获取下载目录".to_string())?
    };
    drop(path_lock);
    
    #[cfg(target_os = "windows")]
    {
        std::process::Command::new("explorer")
            .arg(download_dir)
            .spawn()
            .map_err(|e| format!("打开文件夹失败: {}", e))?;
    }
    
    #[cfg(target_os = "macos")]
    {
        std::process::Command::new("open")
            .arg(download_dir)
            .spawn()
            .map_err(|e| format!("打开文件夹失败: {}", e))?;
    }
    
    #[cfg(target_os = "linux")]
    {
        std::process::Command::new("xdg-open")
            .arg(download_dir)
            .spawn()
            .map_err(|e| format!("打开文件夹失败: {}", e))?;
    }
    
    Ok(())
}

// 下载视频
#[tauri::command]
async fn download_video(video_info: VideoInfo) -> Result<String, String> {
    // 先获取下载目录（在任何 await 之前）
    let download_dir = {
        let path_lock = DOWNLOAD_PATH.lock().map_err(|e| e.to_string())?;
        if let Some(custom_path) = path_lock.as_ref() {
            std::path::PathBuf::from(custom_path)
        } else {
            dirs::download_dir().ok_or("无法获取下载目录".to_string())?
        }
    }; // path_lock 在这里自动释放
    
    // 获取下载URL
    let download_url = get_download_url(&video_info.bvid, video_info.cid).await?;
    
    // 创建HTTP客户端
    let client = reqwest::Client::builder()
        .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
        .build()
        .map_err(|e| format!("创建HTTP客户端失败: {}", e))?;
    
    // 下载视频
    let response = client
        .get(&download_url)
        .header("Referer", "https://www.bilibili.com")
        .send()
        .await
        .map_err(|e| format!("下载请求失败: {}", e))?;
    
    // 清理文件名中的非法字符
    let clean_name = |name: &str| -> String {
        name.chars()
            .map(|c| match c {
                '/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
                _ => c,
            })
            .collect::<String>()
    };
    
    // 创建文件夹（如果是合集）
    let final_dir = if let Some(collection) = &video_info.collection_title {
        let folder_name = clean_name(collection);
        let collection_dir = download_dir.join(&folder_name);
        
        // 创建文件夹
        if !collection_dir.exists() {
            std::fs::create_dir_all(&collection_dir)
                .map_err(|e| format!("创建文件夹失败: {}", e))?;
        }
        
        collection_dir
    } else {
        download_dir
    };
    
    let safe_filename = clean_name(&video_info.title);
    let file_path = final_dir.join(format!("{}.mp4", safe_filename));
    
    // 获取响应内容
    let bytes = response
        .bytes()
        .await
        .map_err(|e| format!("读取响应内容失败: {}", e))?;
    
    // 写入文件
    let mut file = File::create(&file_path)
        .map_err(|e| format!("创建文件失败: {}", e))?;
    
    file.write_all(&bytes)
        .map_err(|e| format!("写入文件失败: {}", e))?;
    
    Ok(file_path.to_string_lossy().to_string())
}

// 旧的问候命令（保留以兼容）
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_opener::init())
        .invoke_handler(tauri::generate_handler![
            greet,
            parse_bilibili_video,
            download_video,
            get_download_path,
            select_download_folder,
            open_download_folder
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
